Complex.C - eine rudimentäre Implementierung komplexer Zahlen
von Jens Gelhar
In manchen Programmiersprachen, z. B. Fortran, kann man direkt mit komplexen Zahlen rechnen. C++ kennt dagegen von Haus aus keine komplexe Zahlen und ähnliche mathematische Features - was aber alles andere als ein Nachteil ist, denn man kann sie sich bei Bedarf leicht selbst implementieren. Das vorliegende Programm stellt eine solche Implementierung dar.
Das Listing gliedert sich ein drei Teile: Eine Schnittstellendefinition mit den Deklarationen der Klasse "Complex" sowie der übrigen Operator-Funktionen, ein Implementationsteil und ein abschließendes Beispielprogramm.
Die wichtigste Deklaration ist die der Klasse "Complex". Das Schlüsselwort "public" gibt hier an, daß alle nachfolgenden Bezeichner von jedem teil des Programms benutzt werden dürfen. Es gibt hier also keine privaten Daten, auf die nur aus der Klasse selbst zugegriffen werden darf.
In der nachfolgenden Zeile werden die Member "r" und "i" für den Real- bzw. Imaginärteil der Zahl deklariert. Das entspricht Struktur-Membern in C oder Record-Feldbezeichnern in Pascal. Interessanter ist die folgende Deklaration einer Funktion, die genau wie die Klasse (nämlich "Complex") heißt und zwei Parameter besitzt. Dies ist ein "Konstruktor", mit dem Objekte des Typs "Complex" automatisch initialisiert werden. Man kann bei der Deklaration eines solchen Objekts also wahlweise Real- und Imaginärteil angeben, z. B.
Complex a(1,0), b(-0.5,1.2);
oder einen oder beide Werte weglassen:
Complex c(42), d;
Das ist deshalb möglich, weil hier für die Parameter Default-Argumente ("= 0") angegeben werden. Der Compiler setzt solche Argumente automatisch ein, wenn bei einem Konstruktor- oder Funktionsaufruf Argumente fehlen.
Den Abschluß der Klassendefinition bildet der Prototyp der Member-Funktion (oder auch "Methode") "conj". Diese Funktion kann auf Objekten des Typs "Complex" aufgerufen werden und leifert dazu die konjugiert-Komplexe. Das angehängte "const" sagt dem Compiler, daß "conj" das Objekt, auf dem es aufgerufen wird, nicht verändert und deshalb auch auf konstante Objekte angewendet werden kann.
Nach der Klassendefinition folgen die Definitionen der Operatoren, die hier überladen werden sollen. Dies sind "+", "-", "*" und "/" für die vier Grundrechenarten sowie die Vergleichsoperationen "==" ("gleich") und "!=" ("ungleich") - da es auf komplexen Zahlen bekanntlich keine vollständige Ordnung gibt, können wir uns "kleiner", "größer" usw. ersparen. Das "&" vor den Parametern gibt an, daß die Argumente hier als Referenz übergeben werden sollen (wie "VAR"-Parameter in Pascal).
Es folgt noch eine interessante Operator-Definition, die vielleicht etwas mehr Erläuterung benötigt. Die Ausgabe von Daten läuft in C++ über die Klasse "ostream, auf der der Operator "<<" überladen ist. Das Objekt "cout" der Klasse "ostream" steht z. B. für die Standard-Ausgabe, und mit Anweisungen wie
cout << "Das Ergebnis ist " << x << ".";
gibt man Daten aller Art auf. Der Programmierer selbst kann "<<" aber weiter überladen und sich so Ausgaberoutinen für eigene Datentypen definieren. In diesem Fall wird eine Ausgaberoutine für die Klasse "Complex" deklariert, so daß auch komplexe Zahlen über "cout" oder andere "Ströme" ausgegeben werden können.
Die eigentliche Implementation der so definierten Funktionen und Operatoren birgt für C-Programmierer nicht viel neues, einmal abgesehen davon, daß bei "conj" der Klassenname ("Complex::") angegeben werden muß - verschiedene Klassen können nämlich gleichnamige Funktionen besitzen - und daß ein Operator wie eine ganz normale Funktion aussieht, nur daß ihr Name eben "operator +" o. Ä. ist.
Das kleine Hauptprogramm macht nicht viel Sinnvolles und soll nur demonstrieren, was uns die Klasse "Complex" bringt. Variablen (also Objekte) der Klasse (also des Typs) "Complex" werden wie vordefinierte numerische Variablen auch deklariert und können direkt mit den Operatoren verknüpft werden.